package cn.patient.pp.module.patients.service;

import cn.hutool.core.util.StrUtil;
import cn.patient.pp.framework.security.core.LoginUser;
import cn.patient.pp.framework.security.core.util.SecurityFrameworkUtils;
import cn.patient.pp.module.patients.controller.admin.vo.importExecl.FollowUpExcelVO;
import cn.patient.pp.module.patients.controller.admin.vo.FollowUpPageReqVO;
import cn.patient.pp.module.patients.controller.admin.vo.FollowUpSaveReqVO;
import cn.patient.pp.module.patients.controller.admin.vo.importExecl.ImportRespVO;
import cn.patient.pp.module.patients.dal.dataobject.patients.PatientsDODO;
import cn.patient.pp.module.patients.dal.mysql.patients.FollowImportExcelMapper;
import cn.patient.pp.module.patients.dal.mysql.patients.PatientsDOMapper;
import cn.patient.pp.module.patients.framework.web.config.Config;
import cn.patient.pp.module.patients.framework.web.config.LogRecordConstants;
import cn.patient.pp.module.patients.framework.web.config.async.AsyncTaskServie;
import cn.patient.pp.module.system.controller.admin.dept.vo.dept.DeptListReqVO;
import cn.patient.pp.module.system.dal.dataobject.dept.DeptDO;
import cn.patient.pp.module.system.dal.dataobject.permission.RoleDO;
import cn.patient.pp.module.system.dal.dataobject.user.AdminUserDO;
import cn.patient.pp.module.system.service.dept.DeptService;
import cn.patient.pp.module.system.service.permission.PermissionService;
import cn.patient.pp.module.system.service.user.AdminUserService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.mzt.logapi.starter.annotation.LogRecord;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;

import org.springframework.validation.annotation.Validated;

import cn.patient.pp.module.patients.dal.dataobject.patients.FollowUpDO;
import cn.patient.pp.framework.common.pojo.PageResult;
import cn.patient.pp.framework.common.util.object.BeanUtils;

import cn.patient.pp.module.patients.dal.mysql.patients.FollowUpMapper;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

import static cn.patient.pp.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.patient.pp.module.infra.enums.ErrorCodeConstants.UP_NOT_EXISTS;
import static cn.patient.pp.module.system.dal.redis.RedisKeyConstants.INCREASE_PROPORTION;
import static cn.patient.pp.module.system.dal.redis.RedisKeyConstants.PATIENTS_PAGE;


/**
 * 随访列表 Service 实现类
 *
 * @author sailv
 */
@Service
@Validated
public class FollowUpServiceImpl implements FollowUpService {

    @Resource
    private FollowUpMapper upMapper;

    @Resource
    private PatientsDOMapper patientsDOMapper;

    @Resource
    private FollowImportExcelMapper followImportExcelMapper;

    @Resource
    private DeptService deptService;

    @Resource
    private AdminUserService adminUserService;

    @Resource
    private PermissionService permissionService;



    @Override
    public Integer createUp(FollowUpSaveReqVO createReqVO) {
        // 插入
        FollowUpDO up = BeanUtils.toBean(createReqVO, FollowUpDO.class);
        upMapper.insert(up);
        // 返回
        return up.getId();
    }

    @Override
    public void updateUp(FollowUpSaveReqVO updateReqVO) {
        // 校验存在
        validateUpExists(updateReqVO.getId());
        // 更新
        FollowUpDO updateObj = BeanUtils.toBean(updateReqVO, FollowUpDO.class);
        upMapper.updateById(updateObj);
    }

    @Override
    public void deleteUp(Integer id) {
        // 校验存在
        validateUpExists(id);
        // 删除
        upMapper.deleteById(id);
    }

    private void validateUpExists(Integer id) {
        if (upMapper.selectById(id) == null) {
            throw exception(UP_NOT_EXISTS);
        }
    }
    @Override
    @LogRecord(success = LogRecordConstants.FOLLOW_UP_DEATIL_SUCCESS,
            operator = "{{#currentUser}}",
            type = LogRecordConstants.FOLLOW_UP,
            fail = "查看详情失败，失败原因：「{{#_errorMsg}}」",
            subType = LogRecordConstants.FOLLOW_UP_DEATIL,bizNo = "{{#id}}")
    public FollowUpDO getUp(Integer id) {
        // —— 1. 查主表
        FollowUpDO followUp = upMapper.selectById(id);

        return followUp;
    }

    @Override
    @LogRecord(success = LogRecordConstants.FOLLOW_UP_PAGE_SUCCESS,
            operator = "{{#currentUser}}",
            type = LogRecordConstants.FOLLOW_UP,
            fail = "查看分页失败，失败原因：「{{#_errorMsg}}」",
            subType = LogRecordConstants.FOLLOW_UP_PAGE,bizNo = LogRecordConstants.FOLLOW_UP_PAGE_BIZ_NO)
    public PageResult<FollowUpDO> getUpPage(FollowUpPageReqVO pageReqVO) {
        //获取登录用户部门
        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
        AdminUserDO user = adminUserService.getUser(loginUser.getId());
//        List<RoleDO> roleDOS = permissionService.getEnableUserRoleListByUserIdFromCache(loginUser.getId());
//        boolean hasPoliceRole = roleDOS.stream()
//                .anyMatch(role -> "police".equals(role.getCode()));
        List<Long> deptIds = deptService.getDeptIdsAndOwnId(user.getDeptId());

        if (pageReqVO.getPatientCode() != null) {
            //由新筛查上报的不允许查随访记录
            if (Config.HAS_DIAGNOSED_PATIENT_CODE.equals(pageReqVO.getPatientCode())){
                return null;
            }else{
                // 查询身份证号
                PatientsDODO patientsDODO = patientsDOMapper.selectOne(new QueryWrapper<PatientsDODO>()
                        .eq("patient_code", pageReqVO.getPatientCode()));
                if (patientsDODO!=null){
                    pageReqVO.setIdNumber(patientsDODO.getIdNumber());
                }
                pageReqVO.setPatientCode(null);
            }

        }

        return upMapper.selectPage(pageReqVO, deptIds);
    }

    @Override
    @LogRecord(success = LogRecordConstants.FOLLOW_UP_IMPORT_SUCCESS,
            operator = "{{#currentUser}}",
            type = LogRecordConstants.FOLLOW_UP,
            fail = "导入基本信息失败，失败原因：「{{#_errorMsg}}」",
            subType = LogRecordConstants.FOLLOW_UP_IMPORT,bizNo = LogRecordConstants.FOLLOW_UP_IMPORT_BIZ_NO)
    @CacheEvict(value = {INCREASE_PROPORTION}, allEntries = true)
    public ImportRespVO importExcel(List<FollowUpExcelVO> list) throws InterruptedException {

        List<DeptDO> deptList = deptService.getDeptList(new DeptListReqVO());

        // 2. 遍历，逐个创建 or 更新
        ImportRespVO respVO = ImportRespVO.builder().createUsernames(new ArrayList<>())
                .updateUsernames(new ArrayList<>()).failureUsernames(new LinkedHashMap<>()).total(new AtomicInteger(0)).build();

        // 保留第一个出现的 DeptDO
        Map<String, Long> deptMap = deptList.stream().collect(
                Collectors.toMap(
                        DeptDO::getName, DeptDO::getId,
                        (existing, replacement) -> existing));

        for (FollowUpExcelVO followUpExcelVO : list) {
            //赋值部门id
            if (StrUtil.isNotBlank(followUpExcelVO.getManagementOrgName())) {
                followUpExcelVO.setDeptId(deptMap.get(followUpExcelVO.getManagementOrgName()));
            }
            //自知力空白默认赋值其他
            if (followUpExcelVO.getInsight() == null || !StrUtil.isNotBlank(followUpExcelVO.getInsight())){
                followUpExcelVO.setInsight("5");
            }
            //目前症状空白赋值无
            if (followUpExcelVO.getCurrentSymptomsDetail() == null ||!StrUtil.isNotBlank(followUpExcelVO.getCurrentSymptomsDetail())){
                //空白默认赋值其他
                followUpExcelVO.setCurrentSymptomsDetail("11");
            }
           //造数据用修改随访时间
//            if (StrUtil.isNotBlank(followUpExcelVO.getFollowUpDate())) {
//               String strings = shiftDate(followUpExcelVO.getFollowUpDate());
//                followUpExcelVO.setFollowUpDate(strings);
//            }
        }

        //切分数据
        List<List<FollowUpExcelVO>> batchList = batchList(list, Config.BATHCH_SIZE);
        //定义线程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                Config.CORE_THREAD_NUM, Config.MAX_THREAD_NUM, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>());

        //将任务提交给线程池执行，同时提供一个队列保存执行结果，你可以“谁先完成谁先处理”。
        CompletionService<Boolean> completionService = new ExecutorCompletionService<>(executor);
        //多线程分批发入数据库
        long start = System.currentTimeMillis(); // 记录开始时间
        for (List<FollowUpExcelVO> batch : batchList) {
            completionService.submit(() -> {
                upMapper.insertIgnoreBatch(batch);
                upMapper.insertUpdateBatch(batch);
                respVO.getTotal().addAndGet(batch.size());
                return true;
            });
        }
        //检查异常
        for (int i = 0; i < batchList.size(); i++) {
            Future<Boolean> result = completionService.take(); // 按完成顺序取出
            long end = System.currentTimeMillis();   // 记录结束时间
            System.out.println("线程[" + Thread.currentThread().getName() + "] 处理耗时：" + (end - start) + " ms");
            try {
                result.get(); // 捕获异常
            } catch (Exception e) {
                e.printStackTrace(); // 可换成记录日志
            }
        }
        executor.shutdown();
        return respVO;
    }

    private <T> List<List<T>> batchList(List<T> list, int batchSize) {
        List<List<T>> batches = new ArrayList<>();
        for (int i = 0; i < list.size(); i += batchSize) {
            int end = Math.min(i + batchSize, list.size());
            batches.add(list.subList(i, end));
        }
        return batches;
    }
    public static String shiftDate(String dateStr) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
        Random random = new Random();
        try {
            Date date = sdf.parse(dateStr);
            int offsetDays = random.nextInt(90) - 50; // -60 到 +60
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(date);
            calendar.add(Calendar.DAY_OF_YEAR, offsetDays);
            return sdf.format(calendar.getTime());
        } catch (ParseException e) {
            throw new IllegalArgumentException("日期格式错误，应为 yyyyMMdd", e);
        }
    }
}